/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.iec.edp.caf.msu.client.discovery;

import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.alibaba.nacos.api.naming.pojo.ListView;
import io.iec.edp.caf.commons.runtime.CafEnvironment;
import io.iec.edp.caf.commons.utils.StringUtils;
import io.iec.edp.caf.msu.api.client.ServiceDiscovery;
import io.iec.edp.caf.msu.api.entity.MsuProperties;
import io.iec.edp.caf.msu.api.entity.ServiceUnitInfo;
import io.iec.edp.caf.msu.client.exception.MsuNotFoundException;
import io.iec.edp.caf.msu.client.exception.ServiceUnitNotFoundException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.env.Environment;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

/**
 * 服务中心-服务发现（Nacos版）
 * 返回一个健康的实例
 *
 * @date 2023-01-30
 */
@Slf4j
public class NacosDiscoveryImpl implements ServiceDiscovery {

    private NamingService namingService;
    private MsuProperties configuration;
    private boolean enableSSL;

    public NacosDiscoveryImpl(NamingService namingService, MsuProperties configuration) {
        this.namingService = namingService;
        this.configuration = configuration;
        this.enableSSL = enableSSL();
    }

    @Override
    public String discover(String serviceUnitName, HashMap<String, String> eventContext) {
        return doDiscover(this.configuration.getApplicationName(), serviceUnitName.toLowerCase());
    }

    @Override
    public List<String> discoverAll(String serviceUnitName, HashMap<String, String> eventContext) {
        return doDiscoverAll(this.configuration.getApplicationName(), serviceUnitName);
    }

    /**
     * 访问 Nacos 获取所有分组下所有MSU信息
     */
    @Override
    public List<ServiceUnitInfo> getEnabledServiceUnitInfo() {
        List<ServiceUnitInfo> infos = new ArrayList<>();
        int pageIndex = 1;      //页数 nacos从第一页开始取
        int pageSize = 1000;    //分页大小
        try {
            //nacos不支持全部获取，递归获取  2211发版前调整为1000批次获取，是否存在获取数据不完整可能
            ListView<String> view = this.namingService.getServicesOfServer(pageIndex, pageSize, this.configuration.getApplicationName());
            for (String su : view.getData()) {
                ServiceUnitInfo info = new ServiceUnitInfo();
                info.setName(su);
                info.setServiceUnitDes(su);
                infos.add(info);
            }
        } catch (NacosException e) {
            log.error("ServiceCenter(Nacos) Failed to get enabledServiceUnits.", e);
            throw new MsuNotFoundException(e);
        }
        return infos;
    }

    private String doDiscover(String namespace, String su) {
        log.info("ServiceCenter(Nacos) Start discovering an instance by su {}", su);

        //实例信息
        try {
            Instance instance = this.namingService.selectOneHealthyInstance(su, namespace);
            log.info("ServiceCenter(Nacos) Current instance: " + instance);
            if (instance != null) {
                String baseAddress = instance.getIp() + ':' + instance.getPort();
                return (this.enableSSL ? "https://" : "http://") + baseAddress;
            }
            throw new ServiceUnitNotFoundException("ServiceCenter(Nacos) Current su " + su + " is not available");
        } catch (Exception e) {
            log.error("ServiceCenter(Nacos) Can't found instance by su {}", su, e);
            throw new ServiceUnitNotFoundException("ServiceCenter(Nacos) Current su " + su + " is not available", e);
        }
    }

    private List<String> doDiscoverAll(String namespace, String serviceUnitName) {
        log.info("ServiceCenter(Nacos) Start discovering all instances by su {}", serviceUnitName);

        List<String> urls = new ArrayList<>();
        try {
            List<Instance> instances = this.namingService.getAllInstances(serviceUnitName, namespace);
            for (Instance instance : instances) {
                String baseAddress = instance.getIp() + ':' + instance.getPort();
                baseAddress = (this.enableSSL ? "https://" : "http://") + baseAddress;
                urls.add(baseAddress);
            }
            log.info("ServiceCenter(Nacos) All instances: {}", urls);
        } catch (Exception e) {
            log.error("ServiceCenter(Nacos) Can't found all instances by su " + serviceUnitName + ", error message " + e.getMessage(), e);
        }
        return urls;
    }

    /**
     * @return 是否开启https
     */
    private boolean enableSSL() {
        Environment environment = CafEnvironment.getEnvironment();
        return !StringUtils.isEmpty(environment.getProperty("server.ssl.key-store"));
    }
}
